영속성 컨텍스트가 엔티티를 생성,수정,삭제 하는 동작을 파악하여 실무 개발할때 삽질을 줄이고 불필요한 쿼리를 발생시키지 않게 한다.
JPA를 활용하여 성능향상을 시킨다.
들어가며
관련 샘플 코드는 Github에 있습니다.
영속성 컨텍스트에 대한 엔티티 관리를 다양한 예제를 만들어보고 피드백을 받아 앞으로 계속 업데이트를 하도록 하겠습니다.
1차 캐시
1 2 3 4 5 6 7 8 9 10 11 12 13
Member member = new Member(); member.setId("member1"); member.setUserName("회원1");
// 1차 캐쉬 저장, 영속성 컨텍스트에 저장 됨 EntityManager em = emf.createEntityManager(); em.getTransaction().begin(); em.persist(member);
// 1차 캐쉬 조회 Member findMember = em.find(Member.class), "member1");
em.getTransaction().end();
DB로 가기전에 먼저 영속성 컨텍스트에 "member1"에 해당하는 데이터가 있으면 우선 조회 한다.
단, 한 트랜잭션내에서만 1차 캐쉬가 보관되어 지는 것이다. 또 동시에 100명에 접속자가 있다면 100개에 영속성 컨텍스트가 생기는 것이며 서로 공유 되지 않는다.
그러면 1차 캐쉬에 없는 데이터는 어떻게 될까??
그때는 DB에서 조회하고 가져온 데이터를 1차 캐쉬에 저장하고 그리고 그 값을 반환한다.
동일성 보장
1 2 3 4
Member a = em.find(Member.class), "member1"); Member b = em.find(Member.class), "member1");
a == b 하면 true 즉, heap 같은 주소를 가지고 있는 객체라는 뜻인데 1차 캐쉬에 보관 된 데이터를 반환하였기 때문에 같은 주소를 가진 데이터 인것이다.
쓰기 지연
DB 트랜잭션 개념이다.
엔티티매니저에 의해서 관리되고 있는 엔티에서 쿼리를 막날려도 DB에 날라가지 않는다. 왜냐면 트랜잭션 상태에서 아직 커밋되지 않았기 때문이다.
flush를 하거나 트랜잭션이 끝나면 그때 DB에 반영된다. 정확히 말하면 트랜잭션이 끝나기전에 JPA가 flush를 하여 영속성 컨텍스트 관리되는 엔티티와 DB와 비교후 변경된것이 있다면 쿼리를 생성하여 쓰기지연 SQL 저장소에 보관하고 그 쿼리를 DB에 반영하고 트랜잭션이 끝나면 커밋이 되어 최종 반영 되는 것이다.
System.out.println("여기서 실제 쿼리 날리지롱~! commit 전"); transaction.commit(); System.out.println("여기서 실제 쿼리 날리지롱~! commit 후"); }
객체지향 쿼리(JPQL, Criteria, QueryDSL) 사용시에는 flush가 발생
밑에 코드에서 query.getResultList() 를 하는 순간 영속화(perist) 3번 하였던 것이 우선 insert를 3번 날리어 DB에 반영하게 됩니다.
왜냐면 그다음 코드에서 select 구문을 날리는데 아무리 쓰기지연이라 하지만 이전에 3개 insert 하는것이 반영이 안되어 select 하는게 의미가 있을까요?? 그래서 JPA에서는 객체지향 쿼리를 사용하게 되면 바로 DB에 반영이 됩니다.
이부분이 2번 insert 쿼리가 DB로 날라간것이다. 그럼 setUserName 메소드 3번 호출한건 왜 Update가 되지 않지??
바로 이 부분이 Psersist Context에 성능 이점이다.
코드를 보면 ryu -> gwang -> boriswinter -> ryu 결국 name은 변하지 않았기 때문에 JPA가 알아서 쿼리를 날리지 않았던 것이다.
이부분이 Dirty Checking 했다는 것이고 DB에 쿼리 날리는 것을 최소화 한다는 개념 Write Behind 개념이다.